﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace Universe.WCF.Behaviors.WcfStreaming
{
    class HashWriterStream : Stream
    {
        public readonly FileStream _fileStream;
        internal LengthReference _lenRef;
        private IResponseStatusStorage _storage;
        private Guid _token;
        private HashAlgorithm _hashAlgorithm;

        public HashWriterStream(FileStream writingFileStream, LengthReference lenRef, Guid token, IResponseStatusStorage storage)
        {
            _fileStream = writingFileStream;
            _lenRef = lenRef;
            _token = token;
            _storage = storage;
        }

        public void RequireHash(HashAlgorithm hashAlgorithm)
        {
            _hashAlgorithm = hashAlgorithm;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _fileStream.Dispose();

                if (_hashAlgorithm != null)
                {
                    _hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                    Hash = _hashAlgorithm.Hash;
                }
            }
        }

        public byte[] Hash;

        public override void Flush()
        {
            _fileStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        private long _prevLen = 0;
        public override void Write(byte[] buffer, int offset, int count)
        {
            _fileStream.Write(buffer, offset, count);
            long newLen = Interlocked.Add(ref _lenRef.Length, count);
            if (count > 0)
                if (_hashAlgorithm != null)
                    _hashAlgorithm.TransformBlock(buffer, offset, count, buffer, offset);

            if (newLen - _prevLen > 8124)
            {
                _prevLen = newLen;
                ResponseStatus status = new ResponseStatus()
                {
                    State = ResponseState.InProgress,
                    Exception = null,
                    Hash = null,
                    Length = newLen,
                    Token = _token
                };
                _storage.StoreResponseStatus(_token, status);
            }

        }

        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { return _lenRef.Length; }
        }

        public override long Position
        {
            get { return _lenRef.Length; }
            set { throw new NotSupportedException(); }
        }
    }

    public class LengthReference
    {
        public long Length;
    }

}
